From: Keir Fraser Date: Fri, 4 Jul 2008 16:52:50 +0000 (+0100) Subject: Add hypercall for adding and removing PCI devices X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~14188^2~102 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=f83baa47ecfe37e392d5bda49ad1e8f242c434a9;p=xen.git Add hypercall for adding and removing PCI devices The add hypercall will add a new PCI device and register it. The remove hypercall will remove the pci_dev strucure for the device. The IOMMU hardware (if present) will be notifed as well. Signed-off-by: Espen Skoglund Signed-off-by: Joshua LeVasseur --- diff --git a/xen/arch/x86/physdev.c b/xen/arch/x86/physdev.c index 75d8fa4dcc..d1e4f39e75 100644 --- a/xen/arch/x86/physdev.c +++ b/xen/arch/x86/physdev.c @@ -500,6 +500,32 @@ ret_t do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg) break; } + case PHYSDEVOP_manage_pci_add: { + struct physdev_manage_pci manage_pci; + ret = -EPERM; + if ( !IS_PRIV(v->domain) ) + break; + ret = -EFAULT; + if ( copy_from_guest(&manage_pci, arg, 1) != 0 ) + break; + + ret = pci_add_device(manage_pci.bus, manage_pci.devfn); + break; + } + + case PHYSDEVOP_manage_pci_remove: { + struct physdev_manage_pci manage_pci; + ret = -EPERM; + if ( !IS_PRIV(v->domain) ) + break; + ret = -EFAULT; + if ( copy_from_guest(&manage_pci, arg, 1) != 0 ) + break; + + ret = pci_remove_device(manage_pci.bus, manage_pci.devfn); + break; + } + default: ret = -ENOSYS; break; diff --git a/xen/drivers/passthrough/amd/pci_amd_iommu.c b/xen/drivers/passthrough/amd/pci_amd_iommu.c index 90ae9c003c..0cc63ae6f6 100644 --- a/xen/drivers/passthrough/amd/pci_amd_iommu.c +++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c @@ -628,6 +628,16 @@ static int amd_iommu_return_device( return reassign_device(s, t, bus, devfn); } +static int amd_iommu_add_device(struct pci_dev *pdev) +{ + return 0; +} + +static int amd_iommu_remove_device(struct pci_dev *pdev) +{ + return 0; +} + static int amd_iommu_group_id(u8 bus, u8 devfn) { int rt; @@ -640,6 +650,8 @@ static int amd_iommu_group_id(u8 bus, u8 devfn) struct iommu_ops amd_iommu_ops = { .init = amd_iommu_domain_init, + .add_device = amd_iommu_add_device, + .remove_device = amd_iommu_remove_device, .assign_device = amd_iommu_assign_device, .teardown = amd_iommu_domain_destroy, .map_page = amd_iommu_map_page, diff --git a/xen/drivers/passthrough/iommu.c b/xen/drivers/passthrough/iommu.c index 2f09a386f0..26719e4b58 100644 --- a/xen/drivers/passthrough/iommu.c +++ b/xen/drivers/passthrough/iommu.c @@ -55,6 +55,32 @@ int iommu_domain_init(struct domain *domain) return hd->platform_ops->init(domain); } +int iommu_add_device(struct pci_dev *pdev) +{ + struct hvm_iommu *hd; + if ( !pdev->domain ) + return -EINVAL; + + hd = domain_hvm_iommu(pdev->domain); + if ( !iommu_enabled || !hd->platform_ops ) + return 0; + + return hd->platform_ops->add_device(pdev); +} + +int iommu_remove_device(struct pci_dev *pdev) +{ + struct hvm_iommu *hd; + if ( !pdev->domain ) + return -EINVAL; + + hd = domain_hvm_iommu(pdev->domain); + if ( !iommu_enabled || !hd->platform_ops ) + return 0; + + return hd->platform_ops->remove_device(pdev); +} + int assign_device(struct domain *d, u8 bus, u8 devfn) { struct hvm_iommu *hd = domain_hvm_iommu(d); diff --git a/xen/drivers/passthrough/pci.c b/xen/drivers/passthrough/pci.c index 4c3b875e94..b692c8e34f 100644 --- a/xen/drivers/passthrough/pci.c +++ b/xen/drivers/passthrough/pci.c @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -93,6 +94,57 @@ struct pci_dev *pci_lock_domain_pdev(struct domain *d, int bus, int devfn) return NULL; } +int pci_add_device(u8 bus, u8 devfn) +{ + struct pci_dev *pdev; + int ret = -ENOMEM; + + write_lock(&pcidevs_lock); + pdev = alloc_pdev(bus, devfn); + if ( !pdev ) + goto out; + + ret = 0; + spin_lock(&pdev->lock); + if ( !pdev->domain ) + { + pdev->domain = dom0; + list_add(&pdev->domain_list, &dom0->arch.pdev_list); + ret = iommu_add_device(pdev); + } + spin_unlock(&pdev->lock); + printk(XENLOG_DEBUG "PCI add device %02x:%02x.%x\n", bus, + PCI_SLOT(devfn), PCI_FUNC(devfn)); + +out: + write_unlock(&pcidevs_lock); + return ret; +} + +int pci_remove_device(u8 bus, u8 devfn) +{ + struct pci_dev *pdev; + int ret = -ENODEV;; + + write_lock(&pcidevs_lock); + list_for_each_entry ( pdev, &alldevs_list, alldevs_list ) + if ( pdev->bus == bus && pdev->devfn == devfn ) + { + spin_lock(&pdev->lock); + ret = iommu_remove_device(pdev); + if ( pdev->domain ) + list_del(&pdev->domain_list); + pci_cleanup_msi(pdev); + free_pdev(pdev); + printk(XENLOG_DEBUG "PCI remove device %02x:%02x.%x\n", bus, + PCI_SLOT(devfn), PCI_FUNC(devfn)); + break; + } + + write_unlock(&pcidevs_lock); + return ret; +} + static void dump_pci_devices(unsigned char ch) { struct pci_dev *pdev; diff --git a/xen/drivers/passthrough/vtd/iommu.c b/xen/drivers/passthrough/vtd/iommu.c index 0def8dc02e..aab25809b7 100644 --- a/xen/drivers/passthrough/vtd/iommu.c +++ b/xen/drivers/passthrough/vtd/iommu.c @@ -1223,13 +1223,15 @@ static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn) switch ( type ) { case DEV_TYPE_PCIe_BRIDGE: - break; - case DEV_TYPE_PCI_BRIDGE: sec_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), PCI_SECONDARY_BUS); sub_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), PCI_SUBORDINATE_BUS); + /*dmar_scope_add_buses(&drhd->scope, sec_bus, sub_bus);*/ + + if ( type == DEV_TYPE_PCIe_BRIDGE ) + break; for ( sub_bus &= 0xff; sec_bus <= sub_bus; sec_bus++ ) { @@ -1308,6 +1310,7 @@ static int domain_context_unmap_one(struct iommu *iommu, u8 bus, u8 devfn) static int domain_context_unmap(u8 bus, u8 devfn) { struct acpi_drhd_unit *drhd; + u16 sec_bus, sub_bus; int ret = 0; u32 type; @@ -1319,10 +1322,14 @@ static int domain_context_unmap(u8 bus, u8 devfn) switch ( type ) { case DEV_TYPE_PCIe_BRIDGE: - break; - case DEV_TYPE_PCI_BRIDGE: - ret = domain_context_unmap_one(drhd->iommu, bus, devfn); + sec_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + PCI_SECONDARY_BUS); + sub_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn), + PCI_SUBORDINATE_BUS); + /*dmar_scope_remove_buses(&drhd->scope, sec_bus, sub_bus);*/ + if ( DEV_TYPE_PCI_BRIDGE ) + ret = domain_context_unmap_one(drhd->iommu, bus, devfn); break; case DEV_TYPE_PCIe_ENDPOINT: @@ -1574,11 +1581,23 @@ static int iommu_prepare_rmrr_dev(struct domain *d, return ret; } +static int intel_iommu_add_device(struct pci_dev *pdev) +{ + if ( !pdev->domain ) + return -EINVAL; + return domain_context_mapping(pdev->domain, pdev->bus, pdev->devfn); +} + +static int intel_iommu_remove_device(struct pci_dev *pdev) +{ + return domain_context_unmap(pdev->bus, pdev->devfn); +} + static void setup_dom0_devices(struct domain *d) { struct hvm_iommu *hd; struct pci_dev *pdev; - int bus, dev, func, ret; + int bus, dev, func; u32 l; hd = domain_hvm_iommu(d); @@ -1599,11 +1618,7 @@ static void setup_dom0_devices(struct domain *d) pdev = alloc_pdev(bus, PCI_DEVFN(dev, func)); pdev->domain = d; list_add(&pdev->domain_list, &d->arch.pdev_list); - - ret = domain_context_mapping(d, pdev->bus, pdev->devfn); - if ( ret != 0 ) - gdprintk(XENLOG_ERR VTDPREFIX, - "domain_context_mapping failed\n"); + domain_context_mapping(d, pdev->bus, pdev->devfn); } } } @@ -1866,6 +1881,8 @@ int iommu_resume(void) struct iommu_ops intel_iommu_ops = { .init = intel_iommu_domain_init, + .add_device = intel_iommu_add_device, + .remove_device = intel_iommu_remove_device, .assign_device = intel_iommu_assign_device, .teardown = iommu_domain_teardown, .map_page = intel_iommu_map_page, diff --git a/xen/include/public/physdev.h b/xen/include/public/physdev.h index 789d965f6c..77aedf546c 100644 --- a/xen/include/public/physdev.h +++ b/xen/include/public/physdev.h @@ -154,6 +154,17 @@ struct physdev_unmap_pirq { typedef struct physdev_unmap_pirq physdev_unmap_pirq_t; DEFINE_XEN_GUEST_HANDLE(physdev_unmap_pirq_t); +#define PHYSDEVOP_manage_pci_add 15 +#define PHYSDEVOP_manage_pci_remove 16 +struct physdev_manage_pci { + /* IN */ + uint8_t bus; + uint8_t devfn; +}; + +typedef struct physdev_manage_pci physdev_manage_pci_t; +DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_t); + /* * Argument to physdev_op_compat() hypercall. Superceded by new physdev_op() * hypercall since 0x00030202. diff --git a/xen/include/xen/iommu.h b/xen/include/xen/iommu.h index 833f5c67d7..390fb9e4d1 100644 --- a/xen/include/xen/iommu.h +++ b/xen/include/xen/iommu.h @@ -56,8 +56,8 @@ struct iommu { struct intel_iommu *intel; }; -int iommu_add_device(u8 bus, u8 devfn); -void iommu_remove_device(u8 bus, u8 devfn); +int iommu_add_device(struct pci_dev *pdev); +int iommu_remove_device(struct pci_dev *pdev); int iommu_domain_init(struct domain *d); void iommu_domain_destroy(struct domain *d); int device_assigned(u8 bus, u8 devfn); @@ -94,6 +94,8 @@ int domain_set_irq_dpci(struct domain *domain, struct hvm_irq_dpci *dpci); struct iommu_ops { int (*init)(struct domain *d); + int (*add_device)(struct pci_dev *pdev); + int (*remove_device)(struct pci_dev *pdev); int (*assign_device)(struct domain *d, u8 bus, u8 devfn); void (*teardown)(struct domain *d); int (*map_page)(struct domain *d, unsigned long gfn, unsigned long mfn); diff --git a/xen/include/xen/pci.h b/xen/include/xen/pci.h index bf7c066fe8..f60c5855d7 100644 --- a/xen/include/xen/pci.h +++ b/xen/include/xen/pci.h @@ -56,6 +56,8 @@ void free_pdev(struct pci_dev *pdev); struct pci_dev *pci_lock_pdev(int bus, int devfn); struct pci_dev *pci_lock_domain_pdev(struct domain *d, int bus, int devfn); +int pci_add_device(u8 bus, u8 devfn); +int pci_remove_device(u8 bus, u8 devfn); uint8_t pci_conf_read8( unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg);